package plp.funcoo.expression.structure;

import plp.funcoo.environment.compilation.CompilationEnvironment;
import plp.funcoo.environment.compilation.Environment;
import plp.funcoo.environment.execution.ExecutionContext;
import plp.funcoo.environment.execution.ExecutionEnvironment;
import plp.funcoo.exceptions.ClassAlreadyDeclared;
import plp.funcoo.exceptions.ClassNotDeclared;
import plp.funcoo.exceptions.MethodAlreadDeclared;
import plp.funcoo.exceptions.MethodNotDeclared;
import plp.funcoo.exceptions.ParameterNumberIncorrect;
import plp.funcoo.exceptions.ParameterTypeIncorrect;
import plp.funcoo.exceptions.VarAlreadyDeclared;
import plp.funcoo.exceptions.VarNotDeclared;
import plp.funcoo.expressions.Expression;
import plp.funcoo.types.PrimitiveType;
import plp.funcoo.util.Type;
import plp.funcoo.value.BooleanValue;

public class IfExpression implements Expression{

	private Expression conditionalExpression, expressionIf, expressionElse;
	
	
	public IfExpression(Expression conditionalExpression, Expression expressionIf,Expression expressionElse)
	{
		this.conditionalExpression = conditionalExpression;
		this.expressionIf = expressionIf;
		this.expressionElse = expressionElse;
	}
	
	public IfExpression(Expression conditionalExpression, Expression expressionIf)
	{
		this.conditionalExpression = conditionalExpression;
		this.expressionIf = expressionIf;
		this.expressionElse = null;
	}


	@Override
	public ExecutionEnvironment evaluate(ExecutionEnvironment envi) throws VarNotDeclared, MethodNotDeclared, VarAlreadyDeclared, MethodAlreadDeclared {
		envi = this.conditionalExpression.evaluate(envi);
		BooleanValue aux = (BooleanValue)((ExecutionContext)envi).getValueAnalyzed();
		
		
		if(aux.getValue())
		{
			envi = this.expressionIf.evaluate(envi);
		}
		else if(this.expressionElse != null)
		{
			envi = this.expressionElse.evaluate(envi);
		}		
		return envi;
	}


	@Override
	public boolean typeCheck(CompilationEnvironment envi)
			throws VarAlreadyDeclared, VarNotDeclared, ClassAlreadyDeclared,
			ClassNotDeclared, MethodAlreadDeclared, MethodNotDeclared, ParameterTypeIncorrect, ParameterNumberIncorrect {
		
		if(this.expressionElse == null)
			return firstValidation(envi);
		else 
			return firstValidation(envi) && this.expressionElse.typeCheck(envi);
			
	}
	
	private boolean firstValidation(CompilationEnvironment envi) throws VarAlreadyDeclared, VarNotDeclared, ClassAlreadyDeclared, ClassNotDeclared, MethodAlreadDeclared, MethodNotDeclared, ParameterTypeIncorrect, ParameterNumberIncorrect
	{
		return this.conditionalExpression.typeCheck(envi) && 
				((PrimitiveType)this.conditionalExpression.getType(envi)).isBoolean() &&
				this.expressionIf.typeCheck(envi);
	}

	@Override
	public Type getType(Environment envi) throws VarNotDeclared,
			ClassNotDeclared {
	
		return expressionIf.getType(envi);
	}

	@Override
	public Expression makeCopy() {
	
		if(this.expressionElse != null)
			return new IfExpression(this.conditionalExpression.makeCopy(), this.expressionIf.makeCopy(),this.expressionElse.makeCopy());
		else
			return new IfExpression(this.conditionalExpression.makeCopy(), this.expressionIf.makeCopy());
	}
}
